package com.xmlt.ui.editors.browsereditor;

import java.net.URLEncoder;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.BrowserFunction;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.w3c.dom.Document;

import com.xmlt.ui.builder.BuilderPlugin;
import com.xmlt.ui.server.EditorContext;
import com.xmlt.ui.server.ServerUtils;

public abstract class BrowserEditor extends EditorPart implements
		IResourceChangeListener {

	private Browser browser;
	private boolean isDirty;
	private EditorContext editorContext;

	public BrowserEditor() {
		ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
	}

	@Override
	public void doSave(IProgressMonitor monitor) {

	}

	@Override
	public void doSaveAs() {
	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		if (!(input instanceof IFileEditorInput))
			throw new PartInitException(
					"Invalid Input: Must be IFileEditorInput");
		setSite(site);
		setInput(input);
		setTitle(getTitle());
		IFile file = (IFile) input.getAdapter(IFile.class);
		editorContext = ServerUtils.createEditorContext(file, this);
	}

	@Override
	public boolean isDirty() {
		return isDirty;
	}

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	@Override
	public void createPartControl(Composite parent) {
		FillLayout layout = new FillLayout();
		parent.setLayout(layout);
		browser = new Browser(parent, SWT.NONE);
		browser.setUrl(getURL());
		browser.addProgressListener(new ProgressListener() {

			@Override
			public void completed(ProgressEvent event) {
				BrowserEditor.this.setBrowserContent();
			}

			@Override
			public void changed(ProgressEvent event) {
			}
		});
		new MessengerFunction("setDirtyFlag") {
			@Override
			public Object function(Object[] arguments) {
				isDirty = (Boolean) arguments[0];
				firePropertyChange(PROP_DIRTY);
				return true;
			}
		};

		new MessengerFunction("setEditorContents") {
			@Override
			public Object function(Object[] arguments) {
				setContent((String) arguments[0]);
				isDirty = true;
				firePropertyChange(PROP_DIRTY);
				return true;
			}
		};
		addCustomfunctions();
	}

	@Override
	public void setFocus() {
		if (browser != null)
			browser.setFocus();
	}

	@Override
	public String getTitle() {
		if (getEditorInput() != null)
			return getEditorInput().getName();
		
		return "XMLT Template Editor";
	}

	/**
	 * Closes all project files on project close.
	 */
	public void resourceChanged(final IResourceChangeEvent event) {
		if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
			Display.getDefault().asyncExec(new Runnable() {
				public void run() {
					IWorkbenchPage[] pages = getSite().getWorkbenchWindow()
							.getPages();
					for (int i = 0; i < pages.length; i++) {
						if (((FileEditorInput) getEditorInput()).getFile()
								.getProject().equals(event.getResource())) {
							IEditorPart editorPart = pages[i]
									.findEditor(getEditorInput());
							pages[i].closeEditor(editorPart, true);
						}
					}
				}
			});
		} else if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
			Display.getDefault().asyncExec(new Runnable() {

				@Override
				public void run() {
					refresh();
				}
			});
		}
	}
	
	/**
	 * The <code>MultiPageEditorPart</code> implementation of this
	 * <code>IWorkbenchPart</code> method disposes all nested editors.
	 * Subclasses may extend.
	 */
	public void dispose() {
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
		super.dispose();
	}
	
	protected void refresh() {
		browser.setUrl(getURL());
	}

	public Browser getBrowser() {
		return browser;
	}
	
	public EditorContext getEditorContext() {
		return editorContext;
	}

	public void setDirty(boolean dirty) {
		boolean oldDirtyValue = isDirty;
		isDirty = dirty;
		if (dirty && !oldDirtyValue)
			firePropertyChange(PROP_DIRTY);
	}

	protected abstract void setContent(String c);

	protected abstract String getContent();
	
	protected abstract String getURL();

	protected void setBrowserContent() {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				IEditorInput input = getEditorInput();
				IFile file = (IFile) input.getAdapter(IFile.class);
				BuilderPlugin.waitForBuilderInit(file.getProject());
				browser.execute("setContent('" + URLEncoder.encode(getContent()) + "')");
			}
		});
	}

	protected void addCustomfunctions() {
	}

	abstract public class MessengerFunction extends BrowserFunction {
		public MessengerFunction(String name) {
			super(browser, name);
		}

		public abstract Object function(Object[] arguments);

	}

	public abstract Document getEditedDocument();

}
